/*************************************************************************\
*                  Copyright (C) Michael Kerrisk, 2020.                   *
*                                                                         *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the   *
* Free Software Foundation, either version 3 or (at your option) any      *
* later version. This program is distributed without any warranty.  See   *
* the file COPYING.gpl-v3 for details.                                    *
\*************************************************************************/

/* Listing 34-6 */

/* handling_SIGTSTP_ts.cpp // Chj: Using PrnTs instead of printf

   Demonstrate the correct way to catch SIGTSTP and raise it again (so that a
   parent process that is monitoring this program can see that it was stopped
   by SIGTSTP).
*/
#include <signal.h>
#include "tlpi_hdr.h"
#include "PrnTs.h"

static void                             /* Handler for SIGTSTP */
tstpHandler(int sig)
{
	sigset_t tstpMask = {}, prevMask = {};
	struct sigaction sa = {};
	int savedErrno = errno;            /* In case we change 'errno' here */

	PrnTs("Caught SIGTSTP");         /* UNSAFE (see Section 21.1.2) */

	if (signal(SIGTSTP, SIG_DFL) == SIG_ERR)
		errExit("signal");              /* Set handling to default */

	raise(SIGTSTP);                     /* Generate a further SIGTSTP */

	/* Unblock SIGTSTP; the pending SIGTSTP immediately suspends the program */

	sigemptyset(&tstpMask);
	sigaddset(&tstpMask, SIGTSTP);
	if (sigprocmask(SIG_UNBLOCK, &tstpMask, &prevMask) == -1)
		errExit("sigprocmask");

	/* Execution resumes here after SIGCONT */

	if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == -1)
		errExit("sigprocmask");         /* Reblock SIGTSTP */

	sigemptyset(&sa.sa_mask);           /* Reestablish handler */
	sa.sa_flags = SA_RESTART;
	sa.sa_handler = tstpHandler;
	if (sigaction(SIGTSTP, &sa, NULL) == -1)
		errExit("sigaction");

	PrnTs("Exiting SIGTSTP handler");
	errno = savedErrno;
}

int
main(int argc, char *argv[])
{
	struct sigaction sa = {};

	/* Only establish handler for SIGTSTP if it is not being ignored */

	if (sigaction(SIGTSTP, NULL, &sa) == -1)
		errExit("sigaction");

	if (sa.sa_handler != SIG_IGN) {
		sigemptyset(&sa.sa_mask);
		sa.sa_flags = SA_RESTART;
		sa.sa_handler = tstpHandler;
		if (sigaction(SIGTSTP, &sa, NULL) == -1)
			errExit("sigaction");
	}

	for (;;) {                          /* Wait for signals */
		pause();
		PrnTs("Back to main()");
	}
}
